import { InterimSet, InterimSets } from './declare'

/**
 * 通过 CSS 规则选择符文本生成 CSS 规则体的匹配正则
 * @param selectorText CSS规则的选择符文本
 * @param global 使用 RegExp 的 g 标志
 * @see https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions#%E9%80%9A%E8%BF%87%E6%A0%87%E5%BF%97%E8%BF%9B%E8%A1%8C%E9%AB%98%E7%BA%A7%E6%90%9C%E7%B4%A2
 * @returns
 */
export function interimSelectorRegExp(selectorText: string, global: boolean = false) {
  return new RegExp(`${selectorText}\\s*{.*?}\\s*`, global ? 'gs' : 's')
}

/**
 * 将 CSS 规则项转换为规则字符串
 * @param interimSet 单项 CSS 规则配置数组
 * @returns
 */
export function interimSetToString(interimSet: InterimSet) {
  return interimSet[2] ? `${interimSet[0]}: ${interimSet[1]} !important;` : `${interimSet[0]}: ${interimSet[1]};`
}

/**
 * 将 CSS 规则集合转换为规则字符串
 * @param ruleContent CSS 规则集合
 * @returns
 */
export function interimSetsToString(ruleContent: InterimSets) {
  return ruleContent
    .filter((row) => row[1] !== null)
    .map(interimSetToString)
    .join('')
}

/**
 * 生成规则体字符串
 * @param selectorText CSS 规则的选择符文本
 * @param ruleContent CSS 规则 集合/字符串
 * @returns
 */
export function toCSSText(selectorText: string, ruleContent: string | InterimSets) {
  if (Array.isArray(ruleContent)) {
    ruleContent = interimSetsToString(ruleContent)
  }
  return `${selectorText}{${ruleContent}}`
}

/**
 * 从 \<style\>.sheet 中获取指定 CSS 选择符的规则体对象（永远返回最后一个规则体，如果存在的话）
 * @param sheet HTMLStyleElement.sheet
 * @param selectorText CSS 规则的选择符文本
 */
export function interimCssRule(sheet: CSSStyleSheet, selectorText: string) {
  // https://developer.mozilla.org/zh-CN/docs/Web/API/CSSStyleRule
  const rules = Array.from(sheet.cssRules) as CSSStyleRule[]
  // 已存在 selectorText 对应的规则体
  let rule: CSSStyleRule | null = null
  for (let i = rules.length - 1; i > -1; i--) {
    if (rules[i].selectorText === selectorText) {
      rule = rules[i]
      break
    }
  }
  return rule
}

/**
 * 设置 <style> 下 css 规则体
 * @param interim 被操作的 <style> 元素对象
 * @param selectorText CSS 规则的选择符文本
 * @param interimSets CSS 规则集合
 * @example
 * const interim = document.createElement('style')
 * // rule: { font-size: 24px; color: blue !important; }
 * const rule = [
 *    ['font-size', '24px'],
 *    ['color', 'blue', true]
 * ]
 * setInterimCssRule(interim, '.box', rule)
 */
export function setInterimCssRule(interim: HTMLStyleElement, selectorText: string, interimSets: InterimSets) {
  const sheet = interim.sheet
  if (sheet) {
    const rule = interimCssRule(sheet, selectorText)
    if (rule) {
      interimSets.forEach(([name, value, important]) => {
        // https://developer.mozilla.org/zh-CN/docs/Web/API/CSSStyleRule/style
        // https://developer.mozilla.org/zh-CN/docs/Web/API/CSSStyleDeclaration
        value === null ? rule.style.removeProperty(name) : rule.style.setProperty(name, value as string, important ? 'important' : '')
      })
    } else {
      sheet.insertRule(toCSSText(selectorText, interimSets), sheet.cssRules.length)
    }
  } else {
    let text = interim.textContent
    // 可能存在 selectorText 对应的规则体
    if (text && text.length > selectorText.length + 2) {
      const matchs = text.match(interimSelectorRegExp(selectorText, true))
      // 存在 selectorText 对应的规则体
      if (Array.isArray(matchs)) {
        const match = matchs.pop() as string
        const newRuleTexts: string[] = []
        let ruleText = match
        interimSets.forEach((row) => {
          // 将之前的规则从规则体中删除
          ruleText = ruleText.replace(new RegExp(`${row[0]}:\\s*.+?;\\s*`, 'g'), '')
          // 暂存新规则
          if (row[1] !== null) {
            newRuleTexts.push(interimSetToString(row))
          }
        })
        // 追加新规则，生成新的规则体
        if (newRuleTexts.length) {
          ruleText = ruleText.replace('}', `${newRuleTexts.join('')}}`)
        }

        // 替换规则体
        interim.textContent = text.replace(match, ruleText)
      }
    }
    // 追加规则体（不存在 selectorText 对应的规则体）
    interim.textContent += toCSSText(selectorText, interimSets)
  }
}

/**
 * 设置 <style> 下的 css 规则体
 * @param interim 被操作的 <style> 元素对象
 * @param selectorText CSS 规则的选择符文本
 * @param cssContent 字符串形式的 CSS 规则集合
 */
export function setInterimCssRuleByRuleString(interim: HTMLStyleElement, selectorText: string, cssContent: string) {
  const cssText = `${selectorText}{${cssContent}}`
  const sheet = interim.sheet
  if (sheet) {
    sheet.insertRule(cssText, sheet.cssRules.length)
  } else {
    interim.textContent = `${interim.textContent}${cssText}`
  }
}

/**
 * 删除已设置的自定义规则体
 * @param interim 被操作的 <style> 元素对象
 * @param selectorText CSS规则的选择符文本
 */
export function deleteInterimCssRule(interim: HTMLStyleElement, selectorText: string) {
  const sheet = interim.sheet
  if (sheet) {
    const rules = sheet.cssRules
    for (let i = rules.length - 1; i > -1; i--) {
      if ((rules[i] as CSSStyleRule).selectorText === selectorText) {
        sheet.deleteRule(i)
      }
    }
  } else {
    const text = interim.textContent
    if (text && text.length > selectorText.length + 2) {
      interim.textContent = text.replace(interimSelectorRegExp(selectorText, true), '')
    }
  }
}

/**
 * 批量删除 <style> 下的 css 规则体
 * @param interim 被操作的 <style> 元素对象
 * @param selectorText CSS规则的选择符文本集合
 */
export function deleteInterimCssRules(interim: HTMLStyleElement, selectorTexts: string[]) {
  const sheet = interim.sheet
  if (sheet) {
    const rules = sheet.cssRules
    for (let i = 0; i < rules.length; i++) {
      if (selectorTexts.includes((rules[i] as CSSStyleRule).selectorText)) {
        sheet.deleteRule(i)
      }
    }
  } else {
    const text = interim.textContent
    selectorTexts.forEach((selectorText) => {
      if (text && text.length > selectorText.length + 2) {
        interim.textContent = text.replace(interimSelectorRegExp(selectorText, true), '')
      }
    })
  }
}

/**
 * 清除 <style> 下所有的 css 规则体
 * @param interim 被操作的 <style> 元素对象
 */
export function clearInterimCssRule(interim: HTMLStyleElement) {
  const sheet = interim.sheet
  if (sheet) {
    const rules = sheet.cssRules
    while (rules.length) {
      sheet.deleteRule(0)
    }
  } else {
    interim.textContent = ''
  }
}

/**
 * 设置 <style> 下 css 规则体的规则项
 * @param interim 被操作的 <style> 元素对象
 * @param selectorText CSS规则的选择符文本
 * @param name 样式名（规则项）
 * @param value 样式值，为 null 时表示删除
 * @param important 是否设置为 !important
 * @example
 * const interim = document.createElement('style')
 * // 设置 <div class="box"></div> 的 color 值为 red
 * setInterimCssRuleProperty(interim, '.box', 'color', 'red')
 */
export function setInterimCssRuleProperty(
  interim: HTMLStyleElement,
  selectorText: string,
  prop: string,
  value: string | number | null,
  important: boolean = false
) {
  return setInterimCssRule(interim, selectorText, [[prop, value, important]])
}

/**
 * 移除  <style> 下 css 规则体的规则项
 * @param interim 被操作的 <style> 元素对象
 * @param selectorText CSS规则的选择符文本
 * @param prop 样式名（规则项）
 * @example
 * const interim = document.createElement('style')
 * // 移除 <div class="box"></div> 的 color 规则项
 * deleteInterimCssRuleProperty(interim, '.box', 'color')
 */
export function deleteInterimCssRuleProperty(interim: HTMLStyleElement, selectorText: string, prop: string) {
  setInterimCssRuleProperty(interim, selectorText, prop, null)
}

/**
 * 批量移除  <style> 下 css 规则体的规则项
 * @param interim 被操作的 <style> 元素对象
 * @param selectorText CSS规则的选择符文本
 * @param prop 样式名（规则项）
 * @example
 * const interim = document.createElement('style')
 * // 移除 <div class="box"></div> 的 color 和 display 规则项
 * deleteInterimCssRuleProperty(interim, '.box', ['color', 'display'])
 */
export function deleteInterimCssRuleProperties(interim: HTMLStyleElement, selectorText: string, props: string[]) {
  props.forEach((prop) => {
    setInterimCssRuleProperty(interim, selectorText, prop, null)
  })
}
